package org.dromara.system.service.impl.trading.channel;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.system.config.core.RedisDelayMQSender;
import org.dromara.system.converter.MachineConverters;
import org.dromara.system.domain.ProductLineInformation;
import org.dromara.system.domain.TradingRecords;
import org.dromara.system.domain.bo.ld.LDTradingWaterReqDTO;
import org.dromara.system.domain.bo.ld.LDTradingWaterSignReqDTO;
import org.dromara.system.domain.vo.ld.LDTradingWaterRspDTO;
import org.dromara.system.enums.NotificationEnum;
import org.dromara.system.enums.RedisMQConstant;
import org.dromara.system.service.impl.trading.AbstractTradingService;
import org.dromara.system.util.JsonUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Random;

/**
 * @author 王星
 * @date 2024/8/27 下午4:39
 */
@Slf4j
@Service("lDTradingService")
@RequiredArgsConstructor
public class LDTradingService extends AbstractTradingService<Object, LDTradingWaterReqDTO> {

    /**
     * 借记卡最大额
     */
    private static final BigDecimal TWENTY_FIVE = new BigDecimal(25);
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * 请求地址
     */
    static String API = "https://sdmer.umfintech.com/buss-gateway/gate";

    @Resource
    private RedisDelayMQSender redisDelayMQSender;


    public static String generateString() {
        Random random = new Random();
        StringBuilder sb = new StringBuilder(40); // 初始容量为40，足以存储最终字符串
        // 生成两个字母的前缀
        sb.append((char) (random.nextInt(26) + 'A')); // 随机大写字母
        sb.append((char) (random.nextInt(26) + 'A')); // 随机大写字母
        // 生成13位数字
        for (int i = 0; i < 13; i++) {
            sb.append(random.nextInt(10)); // 随机数字[0-9]
        }
        return sb.toString();
    }

    /**
     * rsa公钥解密
     *
     * @param data Base64
     * @return
     * @throws Exception
     */
    @SneakyThrows
    public static String rsaDecrypt(String data, String privateKeyStr) {
        PrivateKey privateKey = getPrivateKey(privateKeyStr);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        out.close();
        // 解密后的内容
        return out.toString(StandardCharsets.UTF_8);
    }

    @SneakyThrows
    public static String rsaDecryptByGB(String data, String privateKeyStr) {
        PrivateKey privateKey = getPrivateKey(privateKeyStr);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        out.close();
        // 解密后的内容
        String result = out.toString(Charset.forName("GB2312"));
        return new String(result.getBytes(StandardCharsets.UTF_8));
    }


    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     * @return
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * RSA加密
     *
     * @param data         待加密数据
     * @param publicKeyStr 公钥
     * @return
     */
    @SneakyThrows
    public static String encrypt(String data, String publicKeyStr) {
        PublicKey publicKey = getPublicKey(publicKeyStr);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
        // 加密后的字符串
        return Base64.encodeBase64String(encryptedData);
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     * @return
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    @Override
    public NotificationEnum getNotificationEnum() {
        return NotificationEnum.LD;
    }

    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    @Override
    public void tradingWaterConvert(LDTradingWaterReqDTO reqDTO) {
        TradingRecords tradingRecords = MachineConverters.INSTANCE.lDRecordsToReceiveDealRecords(reqDTO);
        try {
            tradingRecords.setTradingTime(DateUtil.parse(reqDTO.getLocalDate().concat("T").concat(reqDTO.getLocalTime())));
        } catch (Exception e) {
            tradingRecords.setTradingTime(DateUtil.date());
        }
        R<TradingRecords> tradingRecordsR = dealUserTrading(tradingRecords);
    }

    @Override
    public Object tradingWaterAssemble(String mapBody, Integer mark) {
        log.info("联动交易入参={}", JSONUtil.toJsonStr(mapBody));
        LDTradingWaterSignReqDTO reqDTO = JSONUtil.toBean(JSONUtil.parseObj(mapBody), LDTradingWaterSignReqDTO.class);
        ProductLineInformation productLineInformation = getBrandProductsLineById(mark);
        BusinessException.isNull(productLineInformation, "关联权限不存在");
        LDTradingWaterRspDTO rspDTO = new LDTradingWaterRspDTO();
        LDTradingWaterReqDTO ldTradingWaterReqDTO = JsonUtil.toObject(
            rsaDecrypt(reqDTO.getData(), productLineInformation.getAgentPriKey()), LDTradingWaterReqDTO.class);
        ldTradingWaterReqDTO.setUserNo(reqDTO.getUserNo());
        ldTradingWaterReqDTO.setBrandProductsLineId(mark);
        //添加消息记录
        if (StrUtil.equals(ldTradingWaterReqDTO.getMsgType(), "Y007")) {
            log.info("{}服务费推送！", ldTradingWaterReqDTO.getSn());
        } else {
            if (StrUtil.equals(ldTradingWaterReqDTO.getNoSettleType(), "00")) {
                log.info("{}流量费推送！", ldTradingWaterReqDTO.getSn());
            }
            redisDelayMQSender.send(RedisMQConstant.TRADING_QUEUE,
                JsonUtil.toJSONString(ldTradingWaterReqDTO), getNotificationEnum(), Convert.toStr(ldTradingWaterReqDTO.getSerial()));
        }
        rspDTO.setRet_code(reqDTO.getRpid());
        return new LDTradingWaterRspDTO();
    }
}
